package models;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import java.util.Vector;

import javax.swing.table.DefaultTableModel;

import org.apache.log4j.Logger;

import util.ResultTable;
import util.SystemUtil;

import frame.FeatureHandler;


/**
 * @author Rishi Das Roy
 *
 * @Organization Institute Of Genomics & Integrative Biology
 */
public class ResultByVoting {
	public ResultByVoting()  {

		// TODO Auto-generated constructor stub
	}

	private static Logger log = Logger.getLogger(ResultByVoting.class);

	public static ResultTable decision(BaseModel[] predModel, String filePath,
			FeatureHandler lfh) throws IOException {

		Vector columnNames = new Vector();
		Vector data = new Vector();

		BaseModel KGS1_Non = null;
		BaseModel KGS2_Non = null;
		BaseModel KGS3_Non = null;
		BaseModel KGS1_2 = null;
		BaseModel KGS1_3 = null;
		BaseModel KGS2_3 = null;
		BaseModel FGS4_KGS3Minus = null;

		for (BaseModel model : predModel) {
			if (model.getName().equals("KGS1_Non")) {
				KGS1_Non = model;
			} else if (model.getName().equals("KGS2_Non")) {
				KGS2_Non = model;
			} else if (model.getName().equals("KGS3_Non")) {
				KGS3_Non = model;
			} else if (model.getName().equals("KGS1_2")) {
				KGS1_2 = model;
			} else if (model.getName().equals("KGS1_3")) {
				KGS1_3 = model;
			} else if (model.getName().equals("KGS2_3")) {
				KGS2_3 = model;
			} else if (model.getName().equals("FGS4_KGS3Minus")) {
				FGS4_KGS3Minus = model;
			}
		}

		BufferedWriter lBw = new BufferedWriter(new FileWriter(filePath
				+ "_Result.csv"));
		lBw.write("Input Class,Pred_Vote,Score,Pred_Prob,Score,Pred_BAC_Prob,Score,prot_id"
				+ System.getProperty("line.separator"));
		columnNames.addElement("prot_id");
		columnNames.addElement("Prediction");
		columnNames.addElement("Score");
		columnNames.addElement("Pred_Prob");
		columnNames.addElement("Score");
		columnNames.addElement("Pred_BAC_Prob");
		columnNames.addElement("Score");

		for (int i = 0; i < lfh.getActualLabels().size(); i++) {
			float NGS = 0,OIGL = 0,PDGL = 0, Class3 = 0;
			float weight_NGS = 0,weight_OIGL = 0,weight_PDGL = 0, weight_Class3 = 0;
			int vote_NGS = 0,vote_OIGL = 0,vote_PDGL = 0, vote_Class3 = 0;

			if (KGS1_Non.getResult().getPredictedLabels().get(i) >= 0) {
				vote_OIGL++;
				OIGL += Math.abs(KGS1_Non.getResult().getPredictedLabels().get(i));
				weight_OIGL += Math.abs(KGS1_Non.getResult().getPredictedLabels().get(i))*KGS1_Non.getBAC();
			}else {
				vote_NGS++;
				NGS += Math.abs(KGS1_Non.getResult().getPredictedLabels().get(i));
				weight_NGS += Math.abs(KGS1_Non.getResult().getPredictedLabels().get(i))*KGS1_Non.getBAC();
			}

			if (KGS2_Non.getResult().getPredictedLabels().get(i) >= 0) {
				vote_PDGL++;
				PDGL += Math.abs(KGS2_Non.getResult().getPredictedLabels().get(i));
				weight_PDGL += Math.abs(KGS2_Non.getResult().getPredictedLabels().get(i))*KGS2_Non.getBAC();
			}else {
				vote_NGS++;
				NGS += Math.abs(KGS2_Non.getResult().getPredictedLabels().get(i));
				weight_NGS += Math.abs(KGS2_Non.getResult().getPredictedLabels().get(i))*KGS2_Non.getBAC();
			}

			if (KGS3_Non.getResult().getPredictedLabels().get(i) >= 0) {
				vote_Class3++;
				Class3 += Math.abs(KGS3_Non.getResult().getPredictedLabels().get(i));
				weight_Class3 += Math.abs(KGS3_Non.getResult().getPredictedLabels().get(i))*KGS3_Non.getBAC();
			}else {
				vote_NGS++;
				NGS += Math.abs(KGS3_Non.getResult().getPredictedLabels().get(i));
				weight_NGS += Math.abs(KGS3_Non.getResult().getPredictedLabels().get(i))*KGS3_Non.getBAC();
			}


			if (KGS1_2.getResult().getPredictedLabels().get(i) >= 0) {
				vote_OIGL++;
				OIGL += Math.abs(KGS1_2.getResult().getPredictedLabels().get(i));
				weight_OIGL += Math.abs(KGS1_2.getResult().getPredictedLabels().get(i))*KGS1_2.getBAC();
			}else {
				vote_PDGL++;
				PDGL += Math.abs(KGS1_2.getResult().getPredictedLabels().get(i));
				weight_PDGL += Math.abs(KGS1_2.getResult().getPredictedLabels().get(i))*KGS1_2.getBAC();
			}

			if (KGS1_3.getResult().getPredictedLabels().get(i) >= 0) {
				vote_OIGL++;
				OIGL += Math.abs(KGS1_3.getResult().getPredictedLabels().get(i));
				weight_OIGL += Math.abs(KGS1_3.getResult().getPredictedLabels().get(i))*KGS1_3.getBAC();
			}else {
				vote_Class3++;
				Class3 += Math.abs(KGS1_3.getResult().getPredictedLabels().get(i));
				weight_Class3 += Math.abs(KGS1_3.getResult().getPredictedLabels().get(i))*KGS1_3.getBAC();
			}

			if (KGS2_3.getResult().getPredictedLabels().get(i) >= 0) {
				vote_PDGL++;
				PDGL += Math.abs(KGS2_3.getResult().getPredictedLabels().get(i));
				weight_PDGL += Math.abs(KGS2_3.getResult().getPredictedLabels().get(i))*KGS2_3.getBAC();
			}else {
				vote_Class3++;
				Class3 += Math.abs(KGS2_3.getResult().getPredictedLabels().get(i));
				weight_Class3 += Math.abs(KGS2_3.getResult().getPredictedLabels().get(i))*KGS2_3.getBAC();
			}


			Vector row = new Vector();
			row.addElement(lfh.getId().get(i));

			String lPredictLabel = "";
			if (FGS4_KGS3Minus.getResult().getPredictedLabels().get(i) >= 0) {
				lPredictLabel = FGS4_KGS3Minus.getPosLabel();
			}else {
				lPredictLabel = FGS4_KGS3Minus.getNegLabel();
			}

			double probabilityScore = vote_Class3;

			if(vote_PDGL > probabilityScore){
				lPredictLabel = "PDGL";
				probabilityScore = vote_PDGL;
			}

			if(vote_OIGL > probabilityScore){
				lPredictLabel = "OIGL";
				probabilityScore = vote_OIGL;
			}

			if(vote_NGS > probabilityScore){
				lPredictLabel = "NGS";
				probabilityScore = vote_NGS;
			}
			probabilityScore = probabilityScore/3;

			lBw.write(lPredictLabel + "," + Math.abs(probabilityScore) );


			row.addElement(lPredictLabel);
			row.addElement(SystemUtil.roundTwoDecimals(Math.abs(probabilityScore)));

			//probability
			if (FGS4_KGS3Minus.getResult().getPredictedLabels().get(i) >= 0) {
				lPredictLabel = FGS4_KGS3Minus.getPosLabel();
			}else {
				lPredictLabel = FGS4_KGS3Minus.getNegLabel();
			}
			 probabilityScore = Class3;

			if(PDGL > probabilityScore){
				lPredictLabel = "PDGL";
				probabilityScore = PDGL;
			}

			if(OIGL > probabilityScore){
				lPredictLabel = "OIGL";
				probabilityScore = OIGL;
			}

			if(NGS > probabilityScore){
				lPredictLabel = "NGS";
				probabilityScore = NGS;
			}


			lBw.write(","+lPredictLabel + "," + Math.abs(probabilityScore) );


			row.addElement(lPredictLabel);
			row.addElement(SystemUtil.roundTwoDecimals(Math.abs(probabilityScore)));


			if (FGS4_KGS3Minus.getResult().getPredictedLabels().get(i) >= 0) {
				lPredictLabel = FGS4_KGS3Minus.getPosLabel();
			}else {
				lPredictLabel = FGS4_KGS3Minus.getNegLabel();
			}
			 probabilityScore = weight_Class3;

			if(weight_PDGL > probabilityScore){
				lPredictLabel = "PDGL";
				probabilityScore = weight_PDGL;
			}

			if(weight_OIGL > probabilityScore){
				lPredictLabel = "OIGL";
				probabilityScore = weight_OIGL;
			}

			if(weight_NGS > probabilityScore){
				lPredictLabel = "NGS";
				probabilityScore = weight_NGS;
			}


			lBw.write(","+lPredictLabel + "," + Math.abs(probabilityScore) );


			row.addElement(lPredictLabel);
			row.addElement(SystemUtil.roundTwoDecimals(Math.abs(probabilityScore)));

			data.addElement(row);

			lBw.write("," + lfh.getId().get(i)
					+ System.getProperty("line.separator"));
		}
		lBw.close();
		mFalsePositive = lfh.getNegativeCounts() - mTrueNegative;
		mFalseNegative = lfh.getPositiveCounts() - mTruePositive;
		mTotal = lfh.getActualLabels().size();

//		writeResult(filePath + "_performance.csv");

		DefaultTableModel model = new DefaultTableModel(data, columnNames);
		ResultTable lRT = new ResultTable(model);
		lRT.setName("Vote");
		return lRT;

	}



	private static long mTruePositive = 0;

	private static long mTrueNegative = 0;

	private List<Float> mPredictedLabels;

	private static long mFalsePositive;

	private static long mFalseNegative;

	private static long mTotal;

	public long getFalseNegative() {
		return mFalseNegative;
	}

	public long getFalsePositive() {
		return mFalsePositive;
	}

	public List<Float> getPredictedLabels() {
		return mPredictedLabels;
	}

	public long getTrueNegative() {
		return mTrueNegative;
	}

	public long getTruePositive() {
		return mTruePositive;
	}

	public static void writeResult(String pFilePath) throws IOException {
		BufferedWriter lBw = new BufferedWriter(new FileWriter(pFilePath));
		lBw.write("Confusion Matrix" + System.getProperty("line.separator"));
		lBw.write(",,Actual" + System.getProperty("line.separator"));
		lBw.write(",,GS,NGS,Total" + System.getProperty("line.separator"));
		lBw.write("Predicted,GS," + mTruePositive + "," + mFalsePositive + ","
				+ (mTruePositive + mFalsePositive)
				+ System.getProperty("line.separator"));
		lBw.write("Predicted,NGS," + mFalseNegative + "," + mTrueNegative + ","
				+ (mTrueNegative + mFalseNegative)
				+ System.getProperty("line.separator"));
		lBw.write(",Total," + (mTruePositive + mFalseNegative) + ","
				+ (mTrueNegative + mFalsePositive)
				+ System.getProperty("line.separator"));

		lBw.write("Accuracy ," + getAccuracy()
				+ System.getProperty("line.separator"));
		lBw.write("Balance Accuracy ," + getBAC()
				+ System.getProperty("line.separator"));
		lBw.write("MCC ," + getMcc() + System.getProperty("line.separator"));
		lBw.write("Class,GS,NGS" + System.getProperty("line.separator"));
		lBw.write("Recall ," + getP_Recall() + "," + getN_Recall()
				+ System.getProperty("line.separator"));
		lBw.write("Precision ," + getP_Precision() + "," + getN_Precision()
				+ System.getProperty("line.separator"));
		lBw.write("F-Measure ," + getP_Fmeasure() + "," + getN_Fmeasure()
				+ System.getProperty("line.separator"));
		lBw.close();
	}

	private static float getBAC() {

		return (getP_Recall() + getN_Recall()) / 2;
	}

	private static double getMcc() {
		double square_root = Math.sqrt((mTruePositive + mFalsePositive)
				* (mTruePositive + mFalseNegative)
				* (mTrueNegative + mFalsePositive)
				* (mTrueNegative + mFalseNegative));
		if (square_root <= 0)
			return 0;

		return (mTruePositive * mTrueNegative - mFalsePositive * mFalseNegative)
				/ square_root;
	}

	private static float getAccuracy() {
		return (float) (mTruePositive + mTrueNegative) / mTotal;
	}

	private static float getN_Fmeasure() {
		float denominator = (getN_Precision() + getN_Recall());
		if (denominator <= 0)
			return 0;

		return ((float) 2 * getN_Precision() * getN_Recall() / denominator);
	}

	private static float getP_Fmeasure() {
		float denominator = (getP_Precision() + getP_Recall());
		if (denominator <= 0)
			return 0;

		return ((float) 2 * getP_Precision() * getP_Recall() / denominator);
	}

	private static float getN_Precision() {
		long denominator = (mTrueNegative + mFalseNegative);
		if (denominator <= 0)
			return 0;

		return ((float) mTrueNegative / denominator);
	}

	private static float getP_Precision() {
		long denominator = (mTruePositive + mFalsePositive);
		if (denominator <= 0)
			return 0;

		return ((float) mTruePositive / denominator);
	}

	private static float getN_Recall() {
		long denominator = (mTrueNegative + mFalsePositive);
		if (denominator <= 0)
			return 0;

		return ((float) mTrueNegative / denominator);
	}

	/**
	 * Sensitivity;Recall;
	 * true positive rate, hit rate
	 * @return
	 */
	private static float getP_Recall() {
		long denominator = (mTruePositive + mFalseNegative);
		if (denominator <= 0)
			return 0;

		return ((float) mTruePositive / denominator);
	}
}

